package it.eng.area118.mdocommon.android.util;

import java.math.BigDecimal;
import java.math.BigInteger;

public class NumberUtils {

	public static final Integer INTEGER_ZERO = new Integer(0);

	public static final Integer INTEGER_ONE = new Integer(1);

	public static boolean between(double value, double lower, double upper) {
		return (compare(value, lower) > -1 && compare(value, upper) < 1);
	}

	public static int compare(double lhs, double rhs) {
		if (lhs < rhs) {
			return -1;
		}
		if (lhs > rhs) {
			return +1;
		}
		long lhsBits = Double.doubleToLongBits(lhs);
		long rhsBits = Double.doubleToLongBits(rhs);
		if (lhsBits == rhsBits) {
			return 0;
		}
		if (lhsBits < rhsBits) {
			return -1;
		} else {
			return +1;
		}
	}

	public static int compare(float lhs, float rhs) {
		if (lhs < rhs) {
			return -1;
		}
		if (lhs > rhs) {
			return +1;
		}
		int lhsBits = Float.floatToIntBits(lhs);
		int rhsBits = Float.floatToIntBits(rhs);
		if (lhsBits == rhsBits) {
			return 0;
		}
		if (lhsBits < rhsBits) {
			return -1;
		} else {
			return +1;
		}
	}

	public static BigDecimal createBigDecimal(String val) {
		BigDecimal bd = new BigDecimal(val);
		return bd;
	}

	public static BigInteger createBigInteger(String val) {
		BigInteger bi = new BigInteger(val);
		return bi;
	}

	public static Double createDouble(String val) {
		return Double.valueOf(val);
	}

	public static Float createFloat(String str) {
		if (str == null) {
			return null;
		}
		return Float.valueOf(str);
	}

	public static Integer createInteger(String val) {
		return Integer.decode(val);
	}

	public static Long createLong(String val) {
		return Long.valueOf(val);
	}

	public static Number createNumber(String str) throws NumberFormatException {
		if (str == null) {
			return null;
		}
		if (StringUtils.isBlank(str)) {
			throw new NumberFormatException(
					"A blank string is not a valid number");
		}
		if (str.startsWith("--")) {
			return null;
		}
		if (str.startsWith("0x") || str.startsWith("-0x")) {
			return createInteger(str);
		}
		char lastChar = str.charAt(str.length() - 1);
		String mant;
		String dec;
		String exp;
		int decPos = str.indexOf('.');
		int expPos = str.indexOf('e') + str.indexOf('E') + 1;

		if (decPos > -1) {

			if (expPos > -1) {
				if (expPos < decPos) {
					throw new NumberFormatException(str
							+ " is not a valid number.");
				}
				dec = str.substring(decPos + 1, expPos);
			} else {
				dec = str.substring(decPos + 1);
			}
			mant = str.substring(0, decPos);
		} else {
			if (expPos > -1) {
				mant = str.substring(0, expPos);
			} else {
				mant = str;
			}
			dec = null;
		}
		if (!Character.isDigit(lastChar)) {
			if (expPos > -1 && expPos < str.length() - 1) {
				exp = str.substring(expPos + 1, str.length() - 1);
			} else {
				exp = null;
			}
			// Requesting a specific type..
			String numeric = str.substring(0, str.length() - 1);
			boolean allZeros = isAllZeros(mant) && isAllZeros(exp);
			switch (lastChar) {
			case 'l':
			case 'L':
				if (dec == null
						&& exp == null
						&& (numeric.charAt(0) == '-'
								&& isDigits(numeric.substring(1)) || isDigits(numeric))) {
					try {
						return createLong(numeric);
					} catch (NumberFormatException nfe) {
						// Too big for a long
					}
					return createBigInteger(numeric);

				}
				throw new NumberFormatException(str + " is not a valid number.");
			case 'f':
			case 'F':
				try {
					Float f = NumberUtils.createFloat(numeric);
					if (!(f.isInfinite() || (f.floatValue() == 0.0F && !allZeros))) {
						// If it's too big for a float or the float value = 0
						// and the
						// string
						// has non-zeros in it, then float does not have the
						// precision
						// we want
						return f;
					}

				} catch (NumberFormatException nfe) {
					// ignore the bad number
				}
				// Fall through
			case 'd':
			case 'D':
				try {
					Double d = NumberUtils.createDouble(numeric);
					if (!(d.isInfinite() || (d.floatValue() == 0.0D && !allZeros))) {
						return d;
					}
				} catch (NumberFormatException nfe) {
					// ignore the bad number
				}
				try {
					return createBigDecimal(numeric);
				} catch (NumberFormatException e) {
					// ignore the bad number
				}
				// Fall through
			default:
				throw new NumberFormatException(str + " is not a valid number.");

			}
		} else {
			// User doesn't have a preference on the return type, so let's start
			// small and go from there...
			if (expPos > -1 && expPos < str.length() - 1) {
				exp = str.substring(expPos + 1, str.length());
			} else {
				exp = null;
			}
			if (dec == null && exp == null) {
				// Must be an int,long,bigint
				try {
					return createInteger(str);
				} catch (NumberFormatException nfe) {
					// ignore the bad number
				}
				try {
					return createLong(str);
				} catch (NumberFormatException nfe) {
					// ignore the bad number
				}
				return createBigInteger(str);

			} else {
				// Must be a float,double,BigDec
				boolean allZeros = isAllZeros(mant) && isAllZeros(exp);
				try {
					Float f = createFloat(str);
					if (!(f.isInfinite() || (f.floatValue() == 0.0F && !allZeros))) {
						return f;
					}
				} catch (NumberFormatException nfe) {
					// ignore the bad number
				}
				try {
					Double d = createDouble(str);
					if (!(d.isInfinite() || (d.doubleValue() == 0.0D && !allZeros))) {
						return d;
					}
				} catch (NumberFormatException nfe) {
					// ignore the bad number
				}

				return createBigDecimal(str);
			}
		}
	}

	private static boolean isAllZeros(String str) {
		if (str == null) {
			return true;
		}
		for (int i = str.length() - 1; i >= 0; i--) {
			if (str.charAt(i) != '0') {
				return false;
			}
		}
		return str.length() > 0;
	}

	public static boolean isEven(String str) {
		return isEven(toInt(str));
	}

	public static boolean isEven(int numb) {
		return ((numb % 2) == 0);
	}

	public static boolean isOdd(int numb) {
		return ((numb % 2) != 0);
	}

	public static boolean isOdd(String str) {
		return isOdd(toInt(str));
	}

	public static boolean isDigits(String str) {
		if ((str == null) || (str.length() == 0)) {
			return false;
		}
		for (int i = 0; i < str.length(); i++) {
			if (!Character.isDigit(str.charAt(i))) {
				return false;
			}
		}
		return true;
	}

	public static boolean isNaN(double value) {
		return (compare(value, Double.NaN) == 0);
	}

	public static boolean isNumber(String str) {
		if (StringUtils.isEmpty(str)) {
			return false;
		}
		char[] chars = str.toCharArray();
		int sz = chars.length;
		boolean hasExp = false;
		boolean hasDecPoint = false;
		boolean allowSigns = false;
		boolean foundDigit = false;
		int start = (chars[0] == '-') ? 1 : 0;
		if (sz > start + 1) {
			if (chars[start] == '0' && chars[start + 1] == 'x') {
				int i = start + 2;
				if (i == sz) {
					return false;
				}
				for (; i < chars.length; i++) {
					if ((chars[i] < '0' || chars[i] > '9')
							&& (chars[i] < 'a' || chars[i] > 'f')
							&& (chars[i] < 'A' || chars[i] > 'F')) {
						return false;
					}
				}
				return true;
			}
		}
		sz--;
		int i = start;

		while (i < sz || (i < sz + 1 && allowSigns && !foundDigit)) {
			if (chars[i] >= '0' && chars[i] <= '9') {
				foundDigit = true;
				allowSigns = false;

			} else if (chars[i] == '.') {
				if (hasDecPoint || hasExp) {
					return false;
				}
				hasDecPoint = true;
			} else if (chars[i] == 'e' || chars[i] == 'E') {
				if (hasExp) {
					return false;
				}
				if (!foundDigit) {
					return false;
				}
				hasExp = true;
				allowSigns = true;
			} else if (chars[i] == '+' || chars[i] == '-') {
				if (!allowSigns) {
					return false;
				}
				allowSigns = false;
				foundDigit = false;
			} else {
				return false;
			}
			i++;
		}
		if (i < chars.length) {
			if (chars[i] >= '0' && chars[i] <= '9') {
				return true;
			}
			if (chars[i] == 'e' || chars[i] == 'E') {
				return false;
			}
			if (!allowSigns
					&& (chars[i] == 'd' || chars[i] == 'D' || chars[i] == 'f' || chars[i] == 'F')) {
				return foundDigit;
			}
			if (chars[i] == 'l' || chars[i] == 'L') {
				return foundDigit && !hasExp;
			}
			return false;
		}
		return !allowSigns && foundDigit;
	}

	public static int maximum(int a, int b, int c) {
		if (b > a) {
			a = b;
		}
		if (c > a) {
			a = c;
		}
		return a;
	}

	public static long maximum(long a, long b, long c) {
		if (b > a) {
			a = b;
		}
		if (c > a) {
			a = c;
		}
		return a;
	}

	public static int minimum(int a, int b, int c) {
		if (b < a) {
			a = b;
		}
		if (c < a) {
			a = c;
		}
		return a;
	}

	public static long minimum(long a, long b, long c) {
		if (b < a) {
			a = b;
		}
		if (c < a) {
			a = c;
		}
		return a;
	}

	public static int stringToInt(String str) {
		return toInt(str);
	}

	public static int stringToInt(String str, int defaultValue) {
		return toInt(str, defaultValue);
	}

	public static long stringToLong(String str) {
		return toLong(str);
	}

	public static long stringToLong(String str, long defaultValue) {
		return toLong(str, defaultValue);
	}

	public static double stringToDouble(String str) {
		return toDouble(str);
	}

	public static double stringToDouble(String str, double defaultValue) {
		return toDouble(str, defaultValue);
	}

	public static float stringToFloat(String str) {
		return toFloat(str, 0.0f);
	}

	public static float stringToFloat(String str, float defaultValue) {
		return toFloat(str, defaultValue);
	}

	public static double toDouble(String str) {
		return toDouble(str, 0.0d);
	}

	public static double toDouble(String str, double defaultValue) {
		if (str == null) {
			return defaultValue;
		}
		try {
			return Double.parseDouble(str);
		} catch (NumberFormatException nfe) {
			return defaultValue;
		}
	}

	public static float toFloat(String str) {
		return toFloat(str, 0.0f);
	}

	public static float toFloat(String str, float defaultValue) {
		if (str == null) {
			return defaultValue;
		}
		try {
			return Float.parseFloat(str);
		} catch (NumberFormatException nfe) {
			return defaultValue;
		}
	}

	public static int toInt(String str) {
		return toInt(str, 0);
	}

	public static int toInt(String str, int defaultValue) {
		if (str == null) {
			return defaultValue;
		}
		try {
			return Integer.parseInt(str);
		} catch (NumberFormatException nfe) {
			return defaultValue;
		}
	}

	public static long toLong(String str) {
		return toLong(str, 0L);
	}

	public static long toLong(String str, long defaultValue) {
		if (str == null) {
			return defaultValue;
		}
		try {
			return Long.parseLong(str);
		} catch (NumberFormatException nfe) {
			return defaultValue;
		}
	}

	public static void putLong(byte[] data, int offset, long value) {
		data[offset + 0] = (byte) (value >>> 24);
		data[offset + 1] = (byte) (value >>> 16);
		data[offset + 2] = (byte) (value >>> 8);
		data[offset + 3] = (byte) (value);
	}

	public static long getLong(byte[] data, int offset) {
		return ((((long) data[offset + 0]) & 0xFF) << 24)
				+ ((((long) data[offset + 1]) & 0xFF) << 16)
				+ ((((long) data[offset + 2]) & 0xFF) << 8)
				+ ((((long) data[offset + 3]) & 0xFF));
	}

	public NumberUtils() {
		super();
	}
}
